#include <inc/types.h>
#include <inc/mmu.h>
#include <inc/memlayout.h>
#include <inc/x86.h>
#include <inc/traps.h>

#include <kern/cpu.h>
#include <kern/proc.h>
#include <kern/trap.h>
#include <kern/syscall.h>
#include <kern/buf.h>

#include <inc/stdio.h>
#include <inc/traps.h>

// #define DEBUG_TRAP

// Interrupt descriptor table (shared by all CPUs).
struct gatedesc idt[256];
extern uint32_t vectors[]; // in vectors.S: array of 256 entry pointers

void idt_init(void);

// Initialize the interrupt descriptor table.
void
trap_init(void)
{
	// Your code here.
	//
	// Hints:
	// 1. The macro SETGATE in inc/mmu.h should help you, as well as the
	// T_* defines in inc/traps.h;
	// 2. T_SYSCALL is different from the others.
	// uint32_t sel;
	// uint32_t d = 0;
	bool istrap;
	for (uint32_t i = 0; i < 256; i++) {
		if (2 < i && i < 5) {
			istrap = true;
		} else {
			istrap = false;
		}
		SETGATE(idt[i], istrap, SEG_KCODE << 3, vectors[i], 0);
	}
	SETGATE(idt[T_SYSCALL], true, SEG_KCODE << 3, vectors[T_SYSCALL], DPL_USER); // TODO: trap or interrupt?

	idt_init();
}

void
idt_init(void)
{
	lidt(idt, sizeof(idt));
}

void
trap(struct trapframe *tf)
{
	struct proc *cur_proc = thisproc();
#ifdef DEBUG_TRAP
	cprintf("Interrupt/Trap %d triggered.\n", tf->trapno);
#endif
	if (T_SYSCALL == tf->trapno) {
		// syscall
		cur_proc->tf = tf;
		tf->eax = syscall(tf->eax, tf->edx, tf->ecx, tf->ebx, tf->edi, tf->esi);
		return;
	}

	// IRQ
	switch (tf->trapno) {
		case T_IRQ0 + IRQ_TIMER:
			lapic_eoi();
			if (cur_proc)
			{
				spin_lock(&ptable.lock);
				thisproc()->state = RUNNABLE;
				sched();
				spin_unlock(&ptable.lock);
			}
			break;
		case T_IRQ0 + IRQ_IDE:
			ideintr();
			lapic_eoi();
			break;
		default:
			break;
	}
}
